// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3.
//
// The DSA operations in this package are not implemented using constant-time algorithms.
//
// Deprecated: DSA is a legacy algorithm, and modern alternatives such as
// Ed25519 (implemented by package crypto/ed25519) should be used instead. Keys
// with 1024-bit moduli (L1024N160 parameters) are cryptographically weak, while
// bigger keys are not widely supported. Note that FIPS 186-5 no longer approves
// DSA for signature generation.

// package dsa -- go2cs converted at 2022 March 13 05:34:17 UTC
// import "crypto/dsa" ==> using dsa = go.crypto.dsa_package
// Original source: C:\Program Files\Go\src\crypto\dsa\dsa.go
namespace go.crypto;

using errors = errors_package;
using io = io_package;
using big = math.big_package;

using randutil = crypto.@internal.randutil_package;


// Parameters represents the domain parameters for a key. These parameters can
// be shared across many keys. The bit length of Q must be a multiple of 8.

public static partial class dsa_package {

public partial struct Parameters {
    public ptr<big.Int> P;
    public ptr<big.Int> Q;
    public ptr<big.Int> G;
}

// PublicKey represents a DSA public key.
public partial struct PublicKey {
    public ref Parameters Parameters => ref Parameters_val;
    public ptr<big.Int> Y;
}

// PrivateKey represents a DSA private key.
public partial struct PrivateKey {
    public ref PublicKey PublicKey => ref PublicKey_val;
    public ptr<big.Int> X;
}

// ErrInvalidPublicKey results when a public key is not usable by this code.
// FIPS is quite strict about the format of DSA keys, but other code may be
// less so. Thus, when using keys which may have been generated by other code,
// this error must be handled.
public static var ErrInvalidPublicKey = errors.New("crypto/dsa: invalid public key");

// ParameterSizes is an enumeration of the acceptable bit lengths of the primes
// in a set of DSA parameters. See FIPS 186-3, section 4.2.
public partial struct ParameterSizes { // : nint
}

public static readonly ParameterSizes L1024N160 = iota;
public static readonly var L2048N224 = 0;
public static readonly var L2048N256 = 1;
public static readonly var L3072N256 = 2;

// numMRTests is the number of Miller-Rabin primality tests that we perform. We
// pick the largest recommended number from table C.1 of FIPS 186-3.
private static readonly nint numMRTests = 64;

// GenerateParameters puts a random, valid set of DSA parameters into params.
// This function can take many seconds, even on fast machines.


// GenerateParameters puts a random, valid set of DSA parameters into params.
// This function can take many seconds, even on fast machines.
public static error GenerateParameters(ptr<Parameters> _addr_@params, io.Reader rand, ParameterSizes sizes) {
    ref Parameters @params = ref _addr_@params.val;
 
    // This function doesn't follow FIPS 186-3 exactly in that it doesn't
    // use a verification seed to generate the primes. The verification
    // seed doesn't appear to be exported or used by other code and
    // omitting it makes the code cleaner.

    nint L = default;    nint N = default;


    if (sizes == L1024N160) 
        L = 1024;
        N = 160;
    else if (sizes == L2048N224) 
        L = 2048;
        N = 224;
    else if (sizes == L2048N256) 
        L = 2048;
        N = 256;
    else if (sizes == L3072N256) 
        L = 3072;
        N = 256;
    else 
        return error.As(errors.New("crypto/dsa: invalid ParameterSizes"))!;
        var qBytes = make_slice<byte>(N / 8);
    var pBytes = make_slice<byte>(L / 8);

    ptr<big.Int> q = @new<big.Int>();
    ptr<big.Int> p = @new<big.Int>();
    ptr<big.Int> rem = @new<big.Int>();
    ptr<big.Int> one = @new<big.Int>();
    one.SetInt64(1);

GeneratePrimes:

    while (true) {
        {
            var (_, err) = io.ReadFull(rand, qBytes);

            if (err != null) {
                return error.As(err)!;
            }

        }

        qBytes[len(qBytes) - 1] |= 1;
        qBytes[0] |= 0x80;
        q.SetBytes(qBytes);

        if (!q.ProbablyPrime(numMRTests)) {
            continue;
        }
        for (nint i = 0; i < 4 * L; i++) {
            {
                (_, err) = io.ReadFull(rand, pBytes);

                if (err != null) {
                    return error.As(err)!;
                }

            }

            pBytes[len(pBytes) - 1] |= 1;
            pBytes[0] |= 0x80;

            p.SetBytes(pBytes);
            rem.Mod(p, q);
            rem.Sub(rem, one);
            p.Sub(p, rem);
            if (p.BitLen() < L) {
                continue;
            }
            if (!p.ProbablyPrime(numMRTests)) {
                continue;
            }
            @params.P = p;
            @params.Q = q;
            _breakGeneratePrimes = true;
            break;
        }
    }
    ptr<big.Int> h = @new<big.Int>();
    h.SetInt64(2);
    ptr<big.Int> g = @new<big.Int>();

    ptr<big.Int> pm1 = @new<big.Int>().Sub(p, one);
    ptr<big.Int> e = @new<big.Int>().Div(pm1, q);

    while (true) {
        g.Exp(h, e, p);
        if (g.Cmp(one) == 0) {
            h.Add(h, one);
            continue;
        }
        @params.G = g;
        return error.As(null!)!;
    }
}

// GenerateKey generates a public&private key pair. The Parameters of the
// PrivateKey must already be valid (see GenerateParameters).
public static error GenerateKey(ptr<PrivateKey> _addr_priv, io.Reader rand) {
    ref PrivateKey priv = ref _addr_priv.val;

    if (priv.P == null || priv.Q == null || priv.G == null) {
        return error.As(errors.New("crypto/dsa: parameters not set up before generating key"))!;
    }
    ptr<big.Int> x = @new<big.Int>();
    var xBytes = make_slice<byte>(priv.Q.BitLen() / 8);

    while (true) {
        var (_, err) = io.ReadFull(rand, xBytes);
        if (err != null) {
            return error.As(err)!;
        }
        x.SetBytes(xBytes);
        if (x.Sign() != 0 && x.Cmp(priv.Q) < 0) {
            break;
        }
    }

    priv.X = x;
    priv.Y = @new<big.Int>();
    priv.Y.Exp(priv.G, x, priv.P);
    return error.As(null!)!;
}

// fermatInverse calculates the inverse of k in GF(P) using Fermat's method.
// This has better constant-time properties than Euclid's method (implemented
// in math/big.Int.ModInverse) although math/big itself isn't strictly
// constant-time so it's not perfect.
private static ptr<big.Int> fermatInverse(ptr<big.Int> _addr_k, ptr<big.Int> _addr_P) {
    ref big.Int k = ref _addr_k.val;
    ref big.Int P = ref _addr_P.val;

    var two = big.NewInt(2);
    ptr<big.Int> pMinus2 = @new<big.Int>().Sub(P, two);
    return @new<big.Int>().Exp(k, pMinus2, P);
}

// Sign signs an arbitrary length hash (which should be the result of hashing a
// larger message) using the private key, priv. It returns the signature as a
// pair of integers. The security of the private key depends on the entropy of
// rand.
//
// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated
// to the byte-length of the subgroup. This function does not perform that
// truncation itself.
//
// Be aware that calling Sign with an attacker-controlled PrivateKey may
// require an arbitrary amount of CPU.
public static (ptr<big.Int>, ptr<big.Int>, error) Sign(io.Reader rand, ptr<PrivateKey> _addr_priv, slice<byte> hash) {
    ptr<big.Int> r = default!;
    ptr<big.Int> s = default!;
    error err = default!;
    ref PrivateKey priv = ref _addr_priv.val;

    randutil.MaybeReadByte(rand); 

    // FIPS 186-3, section 4.6

    var n = priv.Q.BitLen();
    if (priv.Q.Sign() <= 0 || priv.P.Sign() <= 0 || priv.G.Sign() <= 0 || priv.X.Sign() <= 0 || n % 8 != 0) {
        err = ErrInvalidPublicKey;
        return ;
    }
    n>>=3;

    nint attempts = default;
    for (attempts = 10; attempts > 0; attempts--) {
        ptr<big.Int> k = @new<big.Int>();
        var buf = make_slice<byte>(n);
        while (true) {
            _, err = io.ReadFull(rand, buf);
            if (err != null) {
                return ;
            }
            k.SetBytes(buf); 
            // priv.Q must be >= 128 because the test above
            // requires it to be > 0 and that
            //    ceil(log_2(Q)) mod 8 = 0
            // Thus this loop will quickly terminate.
            if (k.Sign() > 0 && k.Cmp(priv.Q) < 0) {
                break;
            }
        }

        var kInv = fermatInverse(k, _addr_priv.Q);

        r = @new<big.Int>().Exp(priv.G, k, priv.P);
        r.Mod(r, priv.Q);

        if (r.Sign() == 0) {
            continue;
        }
        var z = k.SetBytes(hash);

        s = @new<big.Int>().Mul(priv.X, r);
        s.Add(s, z);
        s.Mod(s, priv.Q);
        s.Mul(s, kInv);
        s.Mod(s, priv.Q);

        if (s.Sign() != 0) {
            break;
        }
    } 

    // Only degenerate private keys will require more than a handful of
    // attempts.
    if (attempts == 0) {
        return (_addr_null!, _addr_null!, error.As(ErrInvalidPublicKey)!);
    }
    return ;
}

// Verify verifies the signature in r, s of hash using the public key, pub. It
// reports whether the signature is valid.
//
// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated
// to the byte-length of the subgroup. This function does not perform that
// truncation itself.
public static bool Verify(ptr<PublicKey> _addr_pub, slice<byte> hash, ptr<big.Int> _addr_r, ptr<big.Int> _addr_s) {
    ref PublicKey pub = ref _addr_pub.val;
    ref big.Int r = ref _addr_r.val;
    ref big.Int s = ref _addr_s.val;
 
    // FIPS 186-3, section 4.7

    if (pub.P.Sign() == 0) {
        return false;
    }
    if (r.Sign() < 1 || r.Cmp(pub.Q) >= 0) {
        return false;
    }
    if (s.Sign() < 1 || s.Cmp(pub.Q) >= 0) {
        return false;
    }
    ptr<big.Int> w = @new<big.Int>().ModInverse(s, pub.Q);
    if (w == null) {
        return false;
    }
    var n = pub.Q.BitLen();
    if (n % 8 != 0) {
        return false;
    }
    ptr<big.Int> z = @new<big.Int>().SetBytes(hash);

    ptr<big.Int> u1 = @new<big.Int>().Mul(z, w);
    u1.Mod(u1, pub.Q);
    var u2 = w.Mul(r, w);
    u2.Mod(u2, pub.Q);
    var v = u1.Exp(pub.G, u1, pub.P);
    u2.Exp(pub.Y, u2, pub.P);
    v.Mul(v, u2);
    v.Mod(v, pub.P);
    v.Mod(v, pub.Q);

    return v.Cmp(r) == 0;
}

} // end dsa_package
